/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.commands.factories;

import java.util.Iterator;

import org.eclipse.emf.common.util.EList;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.InteractionUse;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.Pseudostate;
import org.eclipse.uml2.uml.State;
import org.eclipse.uml2.uml.StateMachine;

import com.cea.papyrus.core.commands.GraphNodeDeleteCommand;
import com.cea.papyrus.core.commands.GraphNodeDeleteWithSemanticParentCommand;
import com.cea.papyrus.core.commands.connection.GraphEdgeDeleteCommand;
import com.cea.papyrus.core.commands.diagram.CloseDiagramCommand;
import com.cea.papyrus.core.commands.diagram.DiagramDeleteCommand;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editor.MultiDiagramEditor;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.DiagramElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * 
 * 
 * @author Remi SCHNEKENBURGER
 */
public class DiagramElementDeleteCommandFactory {

	// make private to force singleton use
	/**
	 * 
	 */
	protected DiagramElementDeleteCommandFactory() {
		
		if (PlatformUI.getWorkbench().getActiveWorkbenchWindow() != null) {
			IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
			
			if ((workbenchWindow.getActivePage() != null)
					&& (workbenchWindow.getActivePage().getActiveEditor() != null)) {
				
				IEditorPart editor = workbenchWindow.getActivePage().getActiveEditor();
				if (editor instanceof MultiDiagramEditor) {
					modelManager = ((MultiDiagramEditor) editor).getModelManager();
				} else {
					//Darpan
					modelManager = (ModelManager) editor.getAdapter(ModelManager.class);
				}
			}
		}
	}

	/**
	 * 
	 */
	protected ModelManager modelManager;

	/**
	 * 
	 */
	public static DiagramElementDeleteCommandFactory eINSTANCE = new DiagramElementDeleteCommandFactory();

	/**
	 * 
	 * 
	 * @param element 
	 * 
	 * @return 
	 */
	public Command createCommand(GraphElement element) {
		CompoundCommand command = new CompoundCommand(); 
		if (element instanceof Diagram) {
			// should verify that the diagram is closed
			Diagram diagram = (Diagram)element;
			if(element.isIsVisible()) {
				command.add(new CloseDiagramCommand(diagram));
			}
			command.add(createCommand((Diagram) element));

		}
		else if (element instanceof GraphNode) {
			if(element.getSemanticParent()!=null){
				command.add(createCommandWithSemanticParent((GraphNode)element));
			}
			else{
				command.add(createCommand((GraphNode) element));
			}
		} else if (element instanceof GraphEdge) {
			command.add(createCommand((GraphEdge) element));
		}
		return command;
	}

	/**
	 * 
	 * 
	 * @param node 
	 * 
	 * @return 
	 */
	protected Command createCommandWithSemanticParent(GraphNode node) {
		CompoundCommand command = new CompoundCommand();
		GraphElement parent;
		GraphElement semancticParent;
		// in the case of a semanticmodel we look for the helper
			//no helper --> basic helper
			parent=node.getContainer();
			semancticParent=(GraphElement)node.getSemanticParent();
		
			// delete all edges from and to this container
		Iterator<GraphConnector> anchors = node.getAnchorage().iterator();
		while (anchors.hasNext()) {
			Iterator<GraphEdge> edges = anchors.next().getGraphEdge().iterator();
			while(edges.hasNext())
			{
				GraphEdge edge = edges.next();
				command.add(new GraphEdgeDeleteCommand(edge));
			}
		}

		//delete indirect subnodes
		Iterator<DiagramElement> it=node.getSemanticChild().iterator();
		while (it.hasNext()){
			GraphElement ge=(GraphElement)it.next();
			command.add(DiagramElementDeleteCommandFactory.eINSTANCE.createCommand(ge));
		}
		command.add(new GraphNodeDeleteWithSemanticParentCommand(parent, node,semancticParent));

		return command;


	}

	/**
	 * 
	 * 
	 * @param diagram 
	 * 
	 * @return 
	 */
	public Command createCommand(Diagram diagram) {
		return new DiagramDeleteCommand(diagram, modelManager);
	}
	
	/**
	 * 
	 * 
	 * @param node 
	 * 
	 * @return 
	 */
	public Command createCommand(GraphNode node) {
		CompoundCommand command = new CompoundCommand();

		GraphElement parent = node.getContainer();
		

		// delete all edges from and to this container
		Iterator<GraphConnector> anchors = node.getAnchorage().iterator();
		while (anchors.hasNext()) {
			Iterator<GraphEdge> edges = anchors.next().getGraphEdge().iterator();
			while(edges.hasNext())
			{
				GraphEdge edge = edges.next();
				command.add(new GraphEdgeDeleteCommand(edge));
			}
		}
		
		
		//delete indirect subnodes
		
		//avoid the case of statemachine because semantic child list is used 
		//to store the list of regions
		if(node.getSemanticModel()!=null){
			if(((Uml1SemanticModelBridge)node.getSemanticModel()).getElement()!=null){
				Element elt = ((Uml1SemanticModelBridge)node.getSemanticModel()).getElement();
				
				// For a lifeline delete covering elements first
				if (elt instanceof Lifeline) {
					command.add(createCommand((Lifeline) elt, node));
				}
				
				if(!(elt instanceof StateMachine)
						&& !((elt instanceof State) && ((State)elt).isComposite())){
					Iterator<DiagramElement> it=node.getSemanticChild().iterator();
					while (it.hasNext()){
						GraphElement ge=(GraphElement)it.next();
						command.add(createCommand(ge));
					}
				}
				//get rid of the pseudostates only for statemachines and composite states
				else{
					Iterator<DiagramElement> it=node.getSemanticChild().iterator();
					while (it.hasNext()){
						GraphElement ge=(GraphElement)it.next();
						if((ge.getSemanticModel() != null) && (((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof Pseudostate))
							command.add(createCommand(ge));
					}
				}
			}
		}

		command.add(new GraphNodeDeleteCommand(parent, node));
		return command;
	}

	/**
	 * 
	 * 
	 * @param edge 
	 * 
	 * @return 
	 */
	public Command createCommand(GraphEdge edge) {
		return new GraphEdgeDeleteCommand(edge);
	}
	
	/**
	 * Delete command for a {@link Lifeline} is handled separately
	 * The command delete elements that are not Graph child of the lifeline node
	 * but that are covering it.
	 * @param lifeline to delete
	 * @param lifelineNode Lifeline GraphNode to delete
	 * @return the deletion (from Diagram) command
	 */
	public Command createCommand(Lifeline lifeline, GraphNode lifelineNode) {
		CompoundCommand command = new CompoundCommand();
		
		// Parse diagram elements and check if the are covering the lifeline
		EList<DiagramElement> diagramElements = lifelineNode.getContainer().getContained();
		Iterator<DiagramElement> it = diagramElements.iterator();
		while (it.hasNext()) {
			DiagramElement currentDElt = it.next();
			if (currentDElt instanceof GraphNode) {
				GraphNode currentGNode = (GraphNode) currentDElt;
				// Retrieve the UML Element associated to the GraphNode
				Element elt = ((Uml1SemanticModelBridge) currentGNode.getSemanticModel()).getElement();
				if (elt != null) {
					
					if (elt instanceof ExecutionSpecification) {
						// Use Start OccurenceSpecification to check if the
						// ExecutionSpecification covers the Lifeline
						if (((ExecutionSpecification) elt).getStart().getCovereds().contains(lifeline)) {
							// Delete the Execution Node
							command.add(createCommand(currentGNode));
						}
						
					} else if ((elt instanceof InteractionFragment)
							&& !(elt instanceof InteractionUse)) {
						// Do not delete InteractionUse
						// It may cover other Interactions in the diagram
						InteractionFragment fragment = (InteractionFragment) elt;
						if ((fragment.getCovereds().size() == 1) 
								&& (fragment.getCovereds().contains(lifeline))) {
							// Delete the InteractionFragment Node
							command.add(createCommand(currentGNode));
						}
					} // else nothing to do
				}
			}
		}
		if (command.isEmpty()) {
			return null;
		}
		return command;
	}

}
